//#include <windows.h>
#include "Server.h"
#include <stdio.h>
#include "Resource.h"
#include "geometry.h"
#include "Coordinator2D.h"
#include "ThreadedComport.h"
#include "WaveViewer.h"
#include <commctrl.h>

#define SIGMA_STATUSBAR_PARTS 3
#define PORT 9909
#define QUEUE_CAP 1024

LRESULT CALLBACK wndProc(HWND, UINT msg, WPARAM, LPARAM);
BOOL CALLBACK dialogProc(HWND, UINT msg, WPARAM, LPARAM);
void onSizeChanged(HWND, WPARAM, LPARAM);
void onPaint(HWND);
void releaseBufferDC();
void onCmdMenu(HWND, UINT msg, WPARAM, LPARAM);
void initStatusBar(HWND);
void onOnBtnOpenClick(HWND);

HINSTANCE hInstance;
HWND hwndStatusBar;
LPCWSTR clsName = L"deluanay";
LPCWSTR wndName = L"Demo building deluanay triangulation";

HDC memoryDC;
RECT clientRect;
HBRUSH oldBrush, bgbrush;
HBITMAP oldBmp, hbmpBuffer;
HPEN oldPen, hPen = NULL;

/* Testing data */
HWND hwndDlgSerial;
Coordinator2D coord;
ThreadedComport comp;
DWORD dwThreadReadId;
Server *server = NULL;
ByteQueue serialQueue(QUEUE_CAP);
ByteQueue tcpQueue(QUEUE_CAP);

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPreInst, LPSTR lpszCmdLine, int nShowCmd)
{
	MSG msg;
	WNDCLASSEX wndcls;
	HWND hwnd;

	hInstance = hInst;

	wndcls.cbSize        = sizeof(WNDCLASSEX);
	wndcls.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
	wndcls.lpfnWndProc   = (WNDPROC) wndProc;
	wndcls.cbClsExtra    = 0;
	wndcls.cbWndExtra    = 0;
	wndcls.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
	wndcls.hCursor       = LoadCursor(NULL, IDC_ARROW);
	wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndcls.lpszMenuName  = MAKEINTRESOURCE(IDR_MAIN_MENU);
	wndcls.lpszClassName = clsName;
	wndcls.hInstance     = hInstance;
	wndcls.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

	RegisterClassEx(&wndcls);

	hwnd = CreateWindow(clsName,
			  wndName,
			  WS_OVERLAPPEDWINDOW,
			  CW_USEDEFAULT,
			  CW_USEDEFAULT,
			  CW_USEDEFAULT,
			  CW_USEDEFAULT,
			  NULL,
			  NULL,
			  hInstance,
			  NULL);

	if(!hwnd)
	{
		MessageBox(NULL, L"Cannot create window", L"Error", MB_OK);
		return 0;
	}

	ShowWindow(hwnd, nShowCmd);
	UpdateWindow(hwnd);

	/* Create status bar */
	initStatusBar(hwnd);

	while( GetMessage(&msg, NULL, 0, 0) )
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

void initStatusBar(HWND hwndMain){
	//Create parts to the status bar
	RECT rcClient;
	LPINT lpParts = NULL;
	int nWidth = 0;
	int nParts = SIGMA_STATUSBAR_PARTS;

	hwndStatusBar = CreateWindowEx(0, // extended not required
                    STATUSCLASSNAME, // status bar class name, equivalent to "msctls_statusbar32"
                    L"", //caption not required
                    WS_CHILD | WS_VISIBLE,
                    -100, // x 
                    -100, // y
                    10, // width
                    10, // height
                    hwndMain,
                    NULL,
                    (HINSTANCE) GetWindowLong (hwndMain, GWL_HINSTANCE),
                    NULL);
	if(hwndStatusBar == INVALID_HANDLE_VALUE)
		return;
         
	// Get the coordinates of the parent window's client area.
	GetClientRect(hwndMain, &rcClient);
         
	// Allocate an array for holding the right edge coordinates.
	HLOCAL hloc = NULL;
	hloc = LocalAlloc(LHND, sizeof(int) * nParts);
	lpParts = (int *)LocalLock(hloc);
         
	// Calculate the right edge coordinate for each part, and
	// copy the coordinates to the array.
	nWidth = rcClient.right / nParts;
	for (int i = 0; i < nParts; i++) 
	{ 
			 lpParts[i] = nWidth;
			 nWidth += nWidth;
	}
         
	// Create status bar parts.
	SendMessage(hwndStatusBar, SB_SETPARTS, (WPARAM)nParts, (LPARAM)lpParts);
         
	// Free the array
	LocalUnlock(hloc);
	LocalFree(hloc);

	/* calculate the location of status bar */
	// re-arrange the client area so that the status bar is always visible
	RECT rectStatusBar;
	GetClientRect(hwndStatusBar, &rectStatusBar); // get status bar client area
	LONG giStatusBarHeight = rectStatusBar.bottom;
	MoveWindow(hwndMain/*ghMDIClientArea*/, 
			 0, 
			 0, 
			 clientRect.right, //width
			 clientRect.bottom - giStatusBarHeight, //height
			 true);
}

LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	UINT cmd;
	BYTE data[5] = {'h', 'e', 'l', 'l', 'o'};
	switch(msg)
	{
		case WM_CREATE:
			if(registerWaveViewerClass(hInstance)==0)
				MessageBox(hwnd, L"Khong the dang ky WaveViewer class", L"Error", MB_OK);
		break;

		case WM_SIZE:
			onSizeChanged(hwnd, wparam, lparam);
			if(hwndStatusBar != NULL)
				SendMessage(hwndStatusBar, WM_SIZE, 0, 0);
			break;

		case WM_LBUTTONDBLCLK:
				if(server != NULL){
					server->broadcase(data, 5);
				}
			break;

		case WM_PAINT:
			onPaint(hwnd);
			break;

		case WM_COMMAND:
			cmd = LOWORD(wparam);
			onCmdMenu(hwnd, cmd, wparam, lparam);
			return TRUE;

		case WM_ERASEBKGND:
			return TRUE;

		case WM_DESTROY:
		case WM_CLOSE:
			//comp.fKeepReading = FALSE;
			//WaitForSingleObject(comp.hReadingThread, INFINITE);
			releaseBufferDC();
			if(server != NULL)
				delete server;
			PostQuitMessage(0);
			break;

		case 1045:
			if(WSAGETSELECTERROR(lparam)){	
				MessageBox(hwnd, L"Connection to server failed", L"Error", MB_OK|MB_ICONERROR);
				//SendMessage(hwnd,WM_DESTROY,NULL,NULL);
				SendMessage(hwndStatusBar, SB_SETTEXT, 0, // Part 1 of status based on Zero based index
					(LPARAM)"Connection to server failed.");
				break;
			}

			switch(WSAGETSELECTEVENT(lparam)){
				case FD_READ:
				break;

				case FD_ACCEPT:
				break;

				case FD_CLOSE:
				break;
			}
			break;

		default:
			return DefWindowProc(hwnd, msg, wparam, lparam);
	}

	return 0;
}

void onPaint(HWND hwnd)
{
	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(hwnd, &ps);	
	//copy from buffer DC to "real" DC
	coord.draw(memoryDC);

	BitBlt(hdc, 0, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, memoryDC, 0, 0, SRCCOPY);
	EndPaint(hwnd, &ps);
}

void onSizeChanged(HWND hwnd, WPARAM wparam, LPARAM lparam)
{
	int height = HIWORD(lparam);
	int width = LOWORD(lparam);

	//
	coord.setViewport(10, 10, width - 10, height/3);
	coord.setScale(.1f, .1f);
	coord.moveTo(15, height/3 - 5);

	releaseBufferDC();
	HDC hdc = GetDC(hwnd);
	GetClientRect(hwnd, &clientRect);
	hbmpBuffer = CreateCompatibleBitmap(hdc, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
	memoryDC = CreateCompatibleDC(hdc);

	bgbrush = CreateSolidBrush(RGB(200, 200, 200));
	oldBrush = (HBRUSH)SelectObject(memoryDC, bgbrush);
	hPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 255));
	oldBmp = (HBITMAP)SelectObject(memoryDC, hbmpBuffer);
	oldPen = (HPEN)SelectObject(memoryDC, hPen);

	FillRect(memoryDC, &clientRect, bgbrush);
	ReleaseDC(hwnd, hdc);
}

void releaseBufferDC()
{
	if(hbmpBuffer != NULL)
	{
		SelectObject(memoryDC, oldBmp);
		SelectObject(memoryDC, oldPen);
		DeleteObject(hbmpBuffer);
		DeleteDC(memoryDC);
		DeleteObject(bgbrush);
		DeleteObject(hPen);
	}
}

void onCmdMenu(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	HWND hctrl;
	switch(msg)
	{
		case ID_CMD_SERIAL_OPEN:
			
			hwndDlgSerial = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), hwnd, (DLGPROC) dialogProc);
			if(hwndDlgSerial != NULL)
				ShowWindow(hwndDlgSerial, SW_SHOW);
			/*
			hctrl = createWaveWindow(hwnd, hInstance);
			if(hctrl == NULL)
				MessageBox(hwnd, L"Khong the tao duoc custom ctrl", L"Error", MB_OK);
			*/

		break;

		case ID_CMD_START_SERVER:
			server = new Server(PORT, &tcpQueue);
			server->setNotifyTo(hwnd);
			if(!server->start()){
				SendMessage(hwndStatusBar, SB_SETTEXT, 0, (LPARAM)L"Server starting failed.");
			}
			else
				SendMessage(hwndStatusBar, SB_SETTEXT, 0, (LPARAM)L"Server started successfully");
		break;
	}
}

VOID requestAddItem2Cbo(HWND hDlg)
{
	HWND hCbo = GetDlgItem(hDlg, IDD_COMPORT_CBO);
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM18"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM17"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM16"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM15"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM14"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM13"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM12"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM11"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM10"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM9"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM8"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM7"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM6"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM5"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM4"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM3"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM2"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"COM1"));
	SendMessage(hCbo, CB_SETCURSEL, 1, 0);

	hCbo = GetDlgItem(hDlg, IDD_BAUDRATE_CBO);
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"4800"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"9600"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"14400"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"19200"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"38400"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"56000"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"57600"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"115200"));
	SendMessage(hCbo, CB_SETCURSEL, 3, 0);

	hCbo = GetDlgItem(hDlg, IDD_DATASIZE_CBO);
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"7"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"8"));
	SendMessage(hCbo, CB_SETCURSEL, 1, 0);

	hCbo = GetDlgItem(hDlg, IDD_PARITY_CBO);
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"None"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"Even"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"Odd"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"Mark"));
	SendMessage(hCbo, CB_SETCURSEL, 0, 0);

	hCbo = GetDlgItem(hDlg, IDD_HANDSHAKE_CBO);
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"OFF"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"RTS/CTS"));
	SendMessage(hCbo, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)L"Xon/Xoff"));
	SendMessage(hCbo, CB_SETCURSEL, 0, 0);
}
BYTE bdata = 0x41;
BOOL CALLBACK dialogProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	HWND cboComport, cboBaudrate, cboDataSize, cboParity;
	HWND btnOpenPort;

	switch(msg)
	{
		case WM_INITDIALOG:
			requestAddItem2Cbo(hwnd);
		return TRUE;
		
		case WM_COMMAND:
			switch(wparam)
			{
				case BTN_OPEN_COMPORT:
					if(!comp.isOpen())
						onOnBtnOpenClick(hwnd);
					else{
						comp.close();
						cboComport = GetDlgItem(hwnd, IDD_COMPORT_CBO);
						btnOpenPort = GetDlgItem(hwnd, BTN_OPEN_COMPORT);
						cboBaudrate = GetDlgItem(hwnd, IDD_BAUDRATE_CBO);
						cboDataSize = GetDlgItem(hwnd, IDD_DATASIZE_CBO);
						cboParity = GetDlgItem(hwnd, IDD_PARITY_CBO);

						SetWindowText(btnOpenPort, L"Open");
						EnableWindow(cboComport, TRUE);
						EnableWindow(cboBaudrate, TRUE);
						EnableWindow(cboDataSize, TRUE);
						EnableWindow(cboParity, TRUE);
					}
				break;

				case ID_BTN_SEND_TEST:
					if(comp.isOpen()){
						comp.write_buff[0] = bdata;
						comp.wlen = 1;
						comp.send_data();
						bdata++;
						if(bdata >= 97)
							bdata = 0x41;
					}
					break;
			}
		break;

		case WM_CLOSE:
			comp.stopReading();
			DestroyWindow(hwnd);
			break;
			//return DefDlgProc(hwnd, msg, wparam, lparam);
	}

	return FALSE;
}

DWORD parseDWORD(TCHAR *str){
	int i = 0;
	DWORD r = 0;
	while(str[i] != NULL){
		r = r * 10 + (str[i] - 48);
		i++;
	}
	return r;
}

void onOnBtnOpenClick(HWND hwnd)
{
	TCHAR temp[16];
	TCHAR pPortPath[32], msg[128];
	DWORD baudrate;

	HWND cboComport, cboBaudrate, cboDataSize, cboParity;
	HWND btnOpenPort, hwndWaveViewer;

	/* Get COM port */
	cboComport = GetDlgItem(hwnd, IDD_COMPORT_CBO);
	GetWindowText(cboComport, temp, 16);
	wsprintf(pPortPath, L"\\\\.\\%s", temp);

	/* Get baudrate */
	cboBaudrate = GetDlgItem(hwnd, IDD_BAUDRATE_CBO);
	GetWindowText(cboBaudrate, temp, 16);
	baudrate = parseDWORD(temp);

	//Kiem tra xem port da mo chua
	if(comp.isOpen())
	{
		wsprintf(msg, L"%s is already openned", temp);
		MessageBox(hwnd, msg, L"Information", MB_OK);
		return;
	}

	//Initialize serial port object
	//if(!comp.open(L"\\\\.\\COM16", 19200, 8, 0))
	if(!comp.open(pPortPath, baudrate, 8, 0))
	{
		wsprintf(msg, L"Cannot open port %s", temp);
		MessageBox(hwnd, msg, L"Error", MB_OK);
		return;
	}

	wsprintf(msg, L"Open port %s with baudrate %ld", pPortPath, baudrate);
	SendMessage(hwndStatusBar, SB_SETTEXT, 0, (LPARAM)msg);

	hwndWaveViewer = GetDlgItem(hwnd, CTRL_WAVEVIEWER);
	comp.setNotifyTo(hwndWaveViewer);
	//comp.setNotifyTo(hwndStatusBar);

	//Tao thread handle viec doc du lieu
	comp.hReadingThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ThreadedComport::recieve_thread_routine, (LPVOID)&comp, 0, &dwThreadReadId);

	btnOpenPort = GetDlgItem(hwnd, BTN_OPEN_COMPORT);
	cboDataSize = GetDlgItem(hwnd, IDD_DATASIZE_CBO);
	cboParity = GetDlgItem(hwnd, IDD_PARITY_CBO);

	SetWindowText(btnOpenPort, L"Close");
	EnableWindow(cboComport, FALSE);
	EnableWindow(cboBaudrate, FALSE);
	EnableWindow(cboDataSize, FALSE);
	EnableWindow(cboParity, FALSE);

}